/*

    ASFTagApp, edit tags in ASF files
    Copyright (C) 2010  Stefano Guidoni <ilguido@users.sf.net>

    ASFTagApp is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

import java.io.RandomAccessFile;
import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Arrays;

// ASFTags class
// ASF files manager
class ASFTags {
  public void tags_reset() {
    tl = 0;
    al = 0;
    cl = 0;
    dl = 0;
    rl = 0; 
  }
  
  public boolean tags_check(String asffile) {
    boolean ret = false;

    try {
      byte[] tmp = new byte[16];
      RandomAccessFile opened_asf = new RandomAccessFile(asffile, "r");

      opened_asf.read(tmp);
      if (Arrays.equals(tmp, ASFID)) {
        ret = true;
      }

      opened_asf.close();
    } catch (IOException ex) {
      System.out.println("Non posso aprire " + asffile + "\n");
      return false;
    }

    return ret;
  }

  public boolean tags_write(String asffile, String tft, String tfa, String tfc, String tfd, String tfv ) {
    int i;
    byte b;
    byte[] ba = new byte[1024];
    byte[] ba64 = new byte[65536];
    byte[] ba1024 = new byte[1048576];
    int cdo_length = 0;
    long file_end = 0;

    if(tft.length() > 0) {
      cdo_length = tft.length() + 1;
      tl = (tft.length() + 1) * 2;
    } else {
      tl = 0;
    }
    if(tfa.length() > 0) {
      cdo_length += tfa.length() + 1;
      al = (tfa.length() + 1) * 2;
    } else {
      al = 0;
    }
    if(tfc.length() > 0) {
      cdo_length += tfc.length() + 1;
      cl = (tfc.length() + 1) * 2;
    } else {
      cl = 0;
    }
    if(tfd.length() > 0) {
      cdo_length += tfd.length() + 1;
      dl = (tfd.length() + 1) * 2;
    } else {
      dl = 0;
    }
    if(tfv.length() > 0) {
      cdo_length += tfv.length() + 1;
      rl = (tfv.length() + 1) * 2;
    } else {
      rl = 0;
    }
    cdo_length += cdo_length;
    if(cdo_length > 0) {
      cdo_length += 34;
      if (cdol == 0) {
        headn++;
      }
    } else {
      if (cdol > 0) {
        headn--;
      } else {
        return false;
      }
    }

    int header_length = headl - cdol + cdo_length;

    ASF_length[0] = (byte)(header_length & 0x000000FF);
    ASF_length[1] = (byte)(header_length >> 8 & 0x000000FF);
    ASF_length[2] = (byte)(header_length >> 16 & 0x000000FF);
    ASF_length[3] = (byte)(header_length >> 24 & 0x000000FF);

    ASF_elems[0] = (byte)(headn & 0x000000FF);
    ASF_elems[1] = (byte)(headn >> 8 & 0x000000FF);

    CDO_length[0] = (byte)(cdo_length & 0x000000FF);
    CDO_length[1] = (byte)(cdo_length >> 8 & 0x000000FF);
    CDO_length[2] = (byte)(cdo_length >> 16 & 0x000000FF);
    CDO_length[3] = (byte)(cdo_length >> 24 & 0x000000FF);

    CDO_title[0] = (byte)(tl & 0x000000FF);
    CDO_title[1] = (byte)(tl >> 8 & 0x000000FF);

    CDO_author[0] = (byte)(al & 0x000000FF);
    CDO_author[1] = (byte)(al >> 8 & 0x000000FF);

    CDO_cr[0] = (byte)(cl & 0x000000FF);
    CDO_cr[1] = (byte)(cl >> 8 & 0x000000FF);

    CDO_descr[0] = (byte)(dl & 0x000000FF);
    CDO_descr[1] = (byte)(dl >> 8 & 0x000000FF);

    CDO_rate[0] = (byte)(rl & 0x000000FF);
    CDO_rate[1] = (byte)(rl >> 8 & 0x000000FF);

    try {
      File f = new File(asffile + ".tmp");
      RandomAccessFile dis2 = new RandomAccessFile(f, "rw");
      RandomAccessFile dis = new RandomAccessFile(asffile, "r");

      for (i = 0; i < 16; i++) {
        b = dis.readByte();
        dis2.writeByte(b);
      }

      for (i = 0; i < 8; i++) {
        dis2.writeByte(ASF_length[i]);
      }

      dis2.writeByte(ASF_elems[0]);
      dis2.writeByte(ASF_elems[1]);
      dis2.writeByte(ASF_elems[2]);
      dis2.writeByte(ASF_elems[3]);

      dis2.writeByte(0x01);
      dis2.writeByte(0x02);
      
      dis.seek(30);

      for (i = 30; i < (dis.length() + header_length - headl); i++) {
        if (i != cursor) {
          b = dis.readByte();
          dis2.writeByte(b);
        } else {
          break;
        }
      }

      if(cdo_length > 0) {

        for (i = 0; i < 16; i++) {
          dis2.writeByte(CDOID[i]);
        }

        for (i = 0; i < 8; i++) {
          dis2.writeByte(CDO_length[i]);
        }
      
        dis2.writeByte(CDO_title[0]);
        dis2.writeByte(CDO_title[1]);

        dis2.writeByte(CDO_author[0]);
        dis2.writeByte(CDO_author[1]);

        dis2.writeByte(CDO_cr[0]);
        dis2.writeByte(CDO_cr[1]);

        dis2.writeByte(CDO_descr[0]);
        dis2.writeByte(CDO_descr[1]);

        dis2.writeByte(CDO_rate[0]);
        dis2.writeByte(CDO_rate[1]);

        if (tl > 0) {
          for (i = 0; i < (tl - 2); i += 2) {
            dis2.writeByte((byte)tft.charAt(i/2));
            dis2.writeByte(0x00);
          }
          dis2.writeByte(0x00);
          dis2.writeByte(0x00);
        }

        if (al > 0) {
          for (i = 0; i < (al - 2); i += 2) {
            dis2.writeByte((byte)tfa.charAt(i/2));
            dis2.writeByte(0x00);
          }
          dis2.writeByte(0x00);
          dis2.writeByte(0x00);
        }

        if (cl > 0) {
          for (i = 0; i < (cl - 2); i += 2) {
            dis2.writeByte((byte)tfc.charAt(i/2));
            dis2.writeByte(0x00);
          }
          dis2.writeByte(0x00);
          dis2.writeByte(0x00);
        }

        if (dl > 0) {
          for (i = 0; i < (dl - 2); i += 2) {
            dis2.writeByte((byte)tfd.charAt(i/2));
            dis2.writeByte(0x00);
          }
          dis2.writeByte(0x00);
          dis2.writeByte(0x00);
        }

        if (rl > 0) {
          for (i = 0; i < (rl - 2); i += 2) {
            dis2.writeByte((byte)tfv.charAt(i/2));
            dis2.writeByte(0x00);
          }
          dis2.writeByte(0x00);
          dis2.writeByte(0x00);
        }

      }

      dis.seek(cursor + cdol);

      file_end = dis.length();
      for (i = cursor + cdol; i < file_end; i++) {
        if ((file_end - i) > 1048576) {
          dis.readFully(ba1024);
          dis2.write(ba1024);
          i += 1048575;
        } else {
          for (; i < file_end; i++) {
            if ((file_end - i) > 65536) {
              dis.readFully(ba64);
              dis2.write(ba64);
              i += 65535;
            } else {
              for (; i < file_end; i++) {
                if ((file_end - i) > 1024) {
                  dis.readFully(ba);
                  dis2.write(ba);
                  i += 1023;
                } else {
                  b = dis.readByte();
                  dis2.writeByte(b);
                }
              }
            }
          }
        }
      }

      cdol = cdo_length;
      headl = header_length;
      dis.close();
      dis2.close();
      return true;
    } catch (IOException ex) {
      System.out.println("Errore di scrittura...\n");
      return false;
    }

  }

  public boolean tags_read(String asffile) {
    int max_pos = 0;
    byte[] tmp = new byte[16];
    cursor = 0;
    try {
      RandomAccessFile dis = new RandomAccessFile(asffile, "r");
      try {
	dis.seek(16);
	dis.readFully(ASF_length);
	dis.readFully(ASF_elems);
	dis.seek(30);
      } catch (IOException ex) {
	System.out.println("Errore di lettura...\n");
	return false;
      }

      max_pos = (((((((int)ASF_length[7] * 256 + (int)ASF_length[6]) * 256 + (int)ASF_length[5]) * 256 + (int)ASF_length[4]) * 256 
                + ((int)ASF_length[3] & 0x000000FF)) * 256 + ((int)ASF_length[2] & 0x000000FF)) * 256 + ((int)ASF_length[1] & 0x000000FF)) * 256 + ((int)ASF_length[0] & 0x000000FF);

      headl = max_pos;
      headn = (((((int)ASF_elems[3] & 0x000000FF)) * 256 + ((int)ASF_elems[2] & 0x000000FF)) * 256 + ((int)ASF_elems[1] & 0x000000FF)) * 256 + ((int)ASF_elems[0] & 0x000000FF);

      for (cursor = 30; cursor < max_pos - 43; cursor++) {
	try {
	  dis.readFully(tmp);
	  dis.seek(cursor);
	} catch (IOException ex) {
	  System.out.println("Errore di lettura...\n");
	  return false;
	}
	if (Arrays.equals(tmp, CDOID)) {
	  break;
	}
      }

      if(cursor >= (max_pos - 43)) {
	cursor = 30;
	cdol = 0;
        tags_reset();
	return true;
      }
      
      try {
	dis.seek(cursor + 16);
	dis.readFully(CDO_length);
	dis.readFully(CDO_title);
	dis.readFully(CDO_author);
	dis.readFully(CDO_cr);
	dis.readFully(CDO_descr);
	dis.readFully(CDO_rate);
      } catch (IOException ex) {
	System.out.println("Errore di lettura...\n");
	return false;
      }

      cdol = (((((((int)CDO_length[7] & 0x000000FF  * 256 + (int)CDO_length[6] & 0x000000FF ) * 256 + (int)CDO_length[5] & 0x000000FF ) * 256 + (int)CDO_length[4] & 0x000000FF ) * 256 
	    + (int)CDO_length[3] & 0x000000FF ) * 256 + (int)CDO_length[2] & 0x000000FF ) * 256 + (int)CDO_length[1] & 0x000000FF ) * 256 + (int)CDO_length[0] & 0x000000FF;
      tl = ((int)CDO_title[1] & 0x000000FF ) * 256 + (int)CDO_title[0] & 0x000000FF ;
      al = ((int)CDO_author[1] & 0x000000FF ) * 256 + (int)CDO_author[0] & 0x000000FF ;
      cl = ((int)CDO_cr[1] & 0x000000FF ) * 256 + (int)CDO_cr[0] & 0x000000FF ;
      dl = ((int)CDO_descr[1] & 0x000000FF ) * 256 + (int)CDO_descr[0] & 0x000000FF ;
      rl = ((int)CDO_rate[1] & 0x000000FF ) * 256 + (int)CDO_rate[0] & 0x000000FF ;

      try {
	dis.readFully(CDO_t);
	dis.seek(cursor + 34 + tl);
	dis.readFully(CDO_a);
	dis.seek(cursor + 34 + tl + al);
	dis.readFully(CDO_c);
	dis.seek(cursor + 34 + tl + al + cl);
	dis.readFully(CDO_d);
	dis.seek(cursor + 34 + tl + al + cl + dl);
	dis.readFully(CDO_r);
      } catch (IOException ex) {
        System.out.println("Errore di lettura...\n");
        return false;
      }
      
      try {
	dis.close();
       } catch (IOException ex) {
	System.out.println("Errore di chiusura...\n");
        return false;
       }

      int i;
      for (i = 0; i < tl - 2; i += 2) {
	lt[i/2] = (char)(CDO_t[i] & 0x000000FF);
      }

      for (i = 0; i < al - 2; i += 2) {
	la[i/2] = (char)(CDO_a[i] & 0x000000FF);
      }

      for (i = 0; i < cl - 2; i += 2) {
	lc[i/2] = (char)(CDO_c[i] & 0x000000FF);
      }

      for (i = 0; i < dl - 2; i += 2) {
	ld[i/2] = (char)(CDO_d[i] & 0x000000FF);
      }

      for (i = 0; i < rl - 2; i += 2) {
	lr[i/2] = (char)(CDO_r[i] & 0x000000FF);
      }
    } catch (FileNotFoundException ex) {
      System.out.println("Errore di apertura..." + asffile +"\n");
    }
    return true;
  }

  private int headl;
  private int headn;
  private int cursor;
  private int cdol;
  public int tl;
  public int al;
  public int cl;
  public int dl;
  public int rl;

  private byte[] ASF_length = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  private byte[] ASF_elems = { 0x00, 0x00, 0x00, 0x00 };
  private byte[] CDO_length = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  private byte[] CDO_title  = { 0x00, 0x00 };
  private byte[] CDO_author = { 0x00, 0x00 };
  private byte[] CDO_cr     = { 0x00, 0x00 };
  private byte[] CDO_descr  = { 0x00, 0x00 };
  private byte[] CDO_rate   = { 0x00, 0x00 };

  private byte[] CDO_t = new byte[128];
  private byte[] CDO_a = new byte[128];
  private byte[] CDO_c = new byte[128];
  private byte[] CDO_d = new byte[128];
  private byte[] CDO_r = new byte[128];

  public char[] lt = new char[64];
  public char[] la = new char[64];
  public char[] lc = new char[64];
  public char[] ld = new char[64];
  public char[] lr = new char[64];

  private byte[] ASFID = {0x30, 0x26, (byte)0xB2, 0x75, (byte)0x8E, 0x66, (byte)0xCF, 0x11, (byte)0xA6, (byte)0xD9, 0x00, (byte)0xAA, 0x00, 0x62, (byte)0xCE, 0x6C};
  private byte[] CDOID = {0x33, 0x26, (byte)0xB2, 0x75, (byte)0x8E, 0x66, (byte)0xCF, 0x11, (byte)0xA6, (byte)0xD9, 0x00, (byte)0xAA, 0x00, 0x62, (byte)0xCE, 0x6C};
}
